//@author A0080442N
#include "Date.h"

Date::Date() {
	_hasDate = false;
	_display = EMPTY_FIELD;
	_dayOfWeek = INITIAL_VALUE;
}

void Date::setDate(int date) {
	if (isValidDate(date)) {
		_year = date % NUM_MOD_HUNDRED;
		_month = (date / NUM_MOD_HUNDRED) % NUM_MOD_HUNDRED;
		_day = (date / NUM_MOD_TEN_THOUSAND) % NUM_MOD_HUNDRED;

		ostringstream display;
		if (_day < NUM_LESS_THAN_TEN) {
			display << NUM_0 << date;
		} else {
			display << date;
		}
		_display = display.str();
		_hasDate = true;
	} else {
		throw INVALID_DATE;
	}
}

void Date::setLocalDate() {
	time_t now = time(INITIAL_VALUE);
	struct tm time;
	localtime_s(&time, &now);

	_day = time.tm_mday;
	_month = time.tm_mon + SHIFT_INDEX_BY_ONE;
	_year = time.tm_year;

	ostringstream display;
	_year = _year % NUM_MOD_HUNDRED;
	setDisplay();
	_hasDate = true;

	_dayOfWeek = time.tm_wday;
	if (_dayOfWeek == INITIAL_VALUE) {
		_dayOfWeek = SEVEN;
	}
}

bool Date::isSameDate(Date secondDate) {
	return (secondDate.getDay() == _day &&
			secondDate.getMonth() == _month &&
			secondDate.getYear() == _year);
}

bool Date::isLaterDate(Date secondDate) {
	if (_hasDate == false && secondDate.getHasDate() == false) {
		return false;
	} else if (_hasDate == false && secondDate.getHasDate() == true) {
		return false;
	} else if (_hasDate == true && secondDate.getHasDate() == false) {
		return true;
	} else if (secondDate.getYear() > _year) {
		return false;
	} else if (secondDate.getYear() == _year && secondDate.getMonth() > _month) {
		return false;
	} else if (secondDate.getYear() == _year && secondDate.getMonth() == _month && secondDate.getDay() > _day) {
		return false;
	}
	return true;
}

bool Date::isValidDate(int date) {
	if (date < INVALID_LOWER_BOUND || date > INVALID_UPPER_BOUND) {
		return false;
	} else {
		int year, month, day;
		
		year = date % NUM_MOD_HUNDRED;
		month = (date / NUM_MOD_HUNDRED) % NUM_MOD_HUNDRED;
		day = (date / NUM_MOD_TEN_THOUSAND) % NUM_MOD_HUNDRED;
		
		if (month > MONTHS_IN_YEARS || month <= INITIAL_VALUE) {
			return false;
		} else if (year % NUM_MOD_FOUR == INITIAL_VALUE) {
			if(day > LEAP_YEAR_DAYS_IN_MONTHS[month - SHIFT_INDEX_BY_ONE] || day <= INITIAL_VALUE) {
				return false;
			}
		} else if (day > DAYS_IN_MONTHS[month - SHIFT_INDEX_BY_ONE] || day <= INITIAL_VALUE) {
				return false;
		}
	}
	return true;
}

void Date::addNumberofDays(int numDay) {
	assert(numDay >= BELOW_ZERO_INDEX);
	if (_hasDate) {
		while (numDay != BELOW_ZERO_INDEX) {
			if ((_day + SHIFT_INDEX_BY_ONE <= LEAP_YEAR_DAYS_IN_MONTHS[_month - SHIFT_INDEX_BY_ONE] && _year % NUM_MOD_FOUR == INITIAL_VALUE) || 
				(_day + SHIFT_INDEX_BY_ONE <= DAYS_IN_MONTHS[_month - SHIFT_INDEX_BY_ONE] && _year % NUM_MOD_FOUR != INITIAL_VALUE)) {
				_day++;
				numDay--;
			} else {
				if (_month + SHIFT_INDEX_BY_ONE <= MONTHS_IN_YEARS) {
					_month++;
					_day = ONE;
					numDay--;
				} else {
					_day = ONE;
					_month = ONE;
					_year++;
					numDay--;
				}
			}
		}
		setDisplay();
	} else {
		throw INVALID_DATE;
	}
}

void Date::minusNumberofDays(int numDay) {
	assert(numDay >= BELOW_ZERO_INDEX);
	if (_hasDate) {
		while (numDay != BELOW_ZERO_INDEX) {
			if (_day - SHIFT_INDEX_BY_ONE != BELOW_ZERO_INDEX) {
				_day--;
				numDay--;
			} else {
				if (_month - SHIFT_INDEX_BY_ONE != BELOW_ZERO_INDEX) {
					numDay--;
					_month--;
					if (_year % NUM_MOD_FOUR == INITIAL_VALUE) {
						_day = LEAP_YEAR_DAYS_IN_MONTHS[_month - SHIFT_INDEX_BY_ONE];
					} else {
						_day = DAYS_IN_MONTHS[_month - SHIFT_INDEX_BY_ONE];
					}
				} else {
					_day = DAYS_IN_MONTH;
					_month = MONTHS_IN_YEARS;
					_year--;
					numDay--;
				}
			}
		}
		setDisplay();
	} else {
		throw INVALID_DATE;
	}
}

int Date::getDayOfWeek() {
	return _dayOfWeek;
}

int Date::getDay() {
	return _day;
}

int Date::getMonth() {
	return _month;
}

int Date::getYear() {
	return _year;
}

string Date::getDisplay() {
	return _display;
}

bool Date::getHasDate() {
	return _hasDate;
}

void Date::setDisplay() {
	ostringstream display;
	if (_day < NUM_LESS_THAN_TEN) {
		display << NUM_0 << _day;
		if (_month < NUM_LESS_THAN_TEN) {
			display << NUM_0 << _month << _year;
		} else {
			display << _month << _year;
		}
	} else {
		display << _day;
		if (_month < NUM_LESS_THAN_TEN) {
			display << NUM_0 << _month << _year;
		} else {
			display << _month << _year;
		}
	}
	_display = display.str();
}